using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;

namespace Framework
{
    [AttributeUsage(AttributeTargets.Field)]
    public class CustomName : PropertyAttribute
    {
        public string Name { get; private set; }
        public CustomName(string name) { Name = name; }
    }

    [AttributeUsage(AttributeTargets.Field)]
    public class ReadOnlyAttribute : PropertyAttribute { }
    
    [AttributeUsage(AttributeTargets.Field)]
    public class ReadOnlyRuntimeAttribute : PropertyAttribute { }

    [AttributeUsage(AttributeTargets.Field)]
    public class ButtonFuncAttribute : PropertyAttribute
    {
        public string ButtonName {get; private set;}
        public string MethodName {get; private set;}
        public ButtonFuncAttribute(string MethodName, string ButtonName = null)
        {
            this.ButtonName = ButtonName;
            this.MethodName = MethodName;
        }
    }
    
    [AttributeUsage(AttributeTargets.Field)]
    public abstract class ConditionalAttribute : PropertyAttribute
    {
        public string ConditionName {get; private set;}
        public bool Hide {get; private set;}

        protected ConditionalAttribute(string conditionName, bool hide)
        {
            ConditionName = conditionName;
            Hide = hide;
        }
        
        public abstract bool ShouldShow(SerializedProperty property);
    }

    public class ConditionalBoolAttribute : ConditionalAttribute
    {
        public ConditionalBoolAttribute(string conditionName, bool hide) : base(conditionName, hide) {}

        public override bool ShouldShow(SerializedProperty condProperty)
        {
            if (condProperty.propertyType == SerializedPropertyType.Boolean) return condProperty.boolValue;
            return true;
        }
    }

    public class ConditionalEnumAttribute : ConditionalAttribute
    {
        private HashSet<int> allowedValues = new();

        public ConditionalEnumAttribute(string conditionName, bool hide, params object[] values) : base(conditionName, hide)
        {
            foreach (var value in values) allowedValues.Add(Convert.ToInt32(value));
        }
        
        public override bool ShouldShow(SerializedProperty condProperty)
        {
            if (condProperty.propertyType == SerializedPropertyType.Enum)
                return allowedValues.Contains(condProperty.enumValueIndex);
            return true;
        }
    }

    public enum CompareMethod
    {
        Greater, GreaterOrEqual, Less, LessOrEqual, Equal, NotEqual
    }
    
    public class ConditionalValueAttribute : ConditionalAttribute
    {
        private bool RangeMode;
        private float MinValue;
        private float MaxValue;
        private CompareMethod Method;
        
        public ConditionalValueAttribute(string conditionName, bool hide, float value, CompareMethod method = CompareMethod.GreaterOrEqual) : base(conditionName, hide)
        {
            MinValue = value;
            Method = method;
        }
        
        public ConditionalValueAttribute(string conditionName, bool hide, float min, float max) : base(conditionName, hide)
        {
            MinValue = min;
            MaxValue = max;
        }
        
        public override bool ShouldShow(SerializedProperty condProperty)
        {
            if (condProperty.propertyType is SerializedPropertyType.Integer or SerializedPropertyType.Float)
            {
                float condValue = condProperty.propertyType is SerializedPropertyType.Float ? condProperty.floatValue : condProperty.intValue;
                if (RangeMode) return condValue >= MinValue && condValue <= MaxValue;
                switch (Method)
                {
                    case CompareMethod.GreaterOrEqual: return condValue >= MinValue;
                    case CompareMethod.Greater: return condValue >= MinValue && condValue <= MaxValue;
                    case CompareMethod.LessOrEqual: return condValue <= MinValue;
                    case CompareMethod.Less: return condValue <= MinValue && condValue >= MaxValue;
                    case CompareMethod.Equal: return Mathf.Approximately(condValue, MinValue);
                    case CompareMethod.NotEqual: return !Mathf.Approximately(condValue, MinValue);
                }
            }
            return true;
        }
    }
}